%{
#include<stdio.h>
#include<stdlib.h>
#include<cstring>
#include<vector>
#include<sstream>
#include<unordered_map>
#include<iostream>
using namespace std;
#ifndef YYSTYPE
#define YYSTYPE string
#endif
unordered_map<string,int>nametoidx;
char idStr[50];
char numStr[50];
int labelcnt;
int yylex();
extern int yyparse();
FILE* yyin;
void yyerror(const char *s);
%}
%token INT
%token WHILE
%token ID
%token NUMBER


%%
compUnit    :   compUnit varDecl {$$=$2;}
            |   compUnit funcDef {$$=$2;}
            |   varDecl {$$=$1;cout<<$$;}
            |   funcDef {$$=$1;cout<<$$;}
            ;
funcDef     :   Type ID '('  ')'block
                {
                    $$="push {fp,lr}\n";
                    $$.append($5);
                    int tol=nametoidx.size();
                    stringstream ss;
                    ss<<"add sp,sp,#"<<tol*4<<"\n";
                    $$.append(ss.str());
                    $$.append("pop {fp,lr}\n");
                    $$.append("bl lr\n");
                }
            ;
block       :   '{' blockItems '}' {$$=$2;}
            ;
blockItems  :   blockItems  blockItem {$$=$1;$$.append($2);}
            |   blockItem   {$$=$1;}
            ;
blockItem   :   varDecl {$$=$1;}
            |   stmt    {$$=$1;}
            ;
varDecl     :   Type varDefList ';'
                {
                    int tol=nametoidx.size();
                    stringstream ss;
                    ss<<"add sp,sp,#-"<<tol*4<<"\n";
                    $$=ss.str();
                    $$.append($2);
                }
            ;
varDefList  :   varDefList ',' valDef
                {
                    $$=$1;
                    $$.append($3);
                }
            |   valDef
                {   
                    $$=$1;
                }
            ;
valDef      :   ID
                {
                    string name=$1;
                    string value="0";
                    if(nametoidx.find(name)==nametoidx.end()){
                        int idx=nametoidx.size()*4;
                        string insts;
                        stringstream ss;
                        ss<<"mov r4,#"<<value<<"\n";
                        ss<<"str r4,[sp,#"<<idx<<"]\n";
                        insts.append(ss.str());
                        nametoidx[name]=idx;
                        $$=insts;
                    }
                    else $$="";

                }
            |   ID '=' NUMBER
                {
                    string name=$1;
                    string value=$3;
                    if(nametoidx.find(name)==nametoidx.end()){
                        int idx=nametoidx.size()*4;
                        string insts;
                        stringstream ss;
                        ss<<"mov r4,#"<<value<<"\n";
                        ss<<"str r4,[sp,#"<<idx<<"]\n";
                        insts.append(ss.str());
                        nametoidx[name]=idx;
                        $$=insts;
                    }
                    else $$="";
                }
            ;
Type        : INT;
stmt        :   WHILE '(' cond ')' block
                {
                    stringstream ss1;
                    ss1<<".Lcond"<<labelcnt<<":\n";
                    stringstream ss2;
                    ss2<<"  .Lcond"<<labelcnt<<"\n";
                    stringstream ss3;
                    ss3<<".Lend"<<labelcnt<<":\n";
                    stringstream ss4;
                    ss4<<"  .Lend"<<labelcnt++<<"\n";
                    $$=ss1.str();
                    $$.append($3);
                    $$.append(ss4.str());
                    $$.append($5);
                    $$.append("b");
                    $$.append(ss2.str());
                    $$.append(ss3.str());
                }
            |   expr ';'    {$$=$1;}
            ;
expr        :   lexpr '+' rexpr
                {
                    $$=$1;
                    $$.append($3);
                    $$.append("add r4,r4,r5\n");
                }
            |   lexpr '-' rexpr
                {
                    $$=$1;
                    $$.append($3);
                    $$.append("sub r4,r4,r5\n"); 
                }
            |   ID '=' expr
                {
                    int idx=nametoidx[$1];
                    stringstream ss;
                    ss<<"str r4,[sp"<<",#"<<idx<<"]\n";
                    $$=$3;
                    $$.append(ss.str()); 
                }
            ;          
lexpr       :   NUMBER
                {
                    stringstream ss;
                    ss<<"mov r4,#"<<$1<<"\n";
                    $$=ss.str();
                }
            |   ID
                {
                    int idx=nametoidx[$1];
                    stringstream ss;
                    ss<<"ldr r4,[sp"<<",#"<<idx<<"]\n";
                    $$=ss.str();
                }
            ;
rexpr       :   NUMBER
                {
                    stringstream ss;
                    ss<<"mov r5,#"<<$1<<"\n";
                    $$=ss.str();
                }
            |   ID
                {
                    int idx=nametoidx[$1];
                    stringstream ss;
                    ss<<"ldr r5,[sp"<<",#"<<idx<<"]\n";
                    $$=ss.str();
                }
            ;
cond        :   lexpr '<' rexpr
                {
                    $$=$1;
                    $$.append($3);
                    $$.append("cmp	r4,r5\n");
                    $$.append("bge");
                }
            |   lexpr '>' rexpr  
                {
                    $$=$1;
                    $$.append($3);
                    $$.append("cmp	r4,r5\n");
                    $$.append("ble");
                }
            ;
%%



int yylex(){
    char c;
    while(1){
        c=getchar();
        if(c==' '||c=='\n'||c=='\t');
        else if(isdigit(c))
        {
            int idx=0;
            while(isdigit(c)){
                numStr[idx++]=c;
                c=getchar();
            }
            numStr[idx]='\0';
            yylval=numStr;
            ungetc(c,stdin);
            return NUMBER;
        }
        else if(isalpha(c)||c=='_'){
            int idx=0;
            while(isalnum(c)||c=='_'){
                idStr[idx++]=c;
                c=getchar();
            }
            idStr[idx]='\0';
            yylval=idStr;
            ungetc(c,stdin);
            if(yylval=="int")
                return INT;
            else if(yylval=="while")
                return WHILE;
            return ID;
        }
        else{
            return c;
        }
        
    }
}

void yyerror(const char* s){
    fprintf(stderr,"parse error:%s\n",s);
    exit(1);
}
int main(){
#ifdef YYDEBUG
  yydebug = 1;
#endif
    yyin=stdin;
    do{
        yyparse();
    }while(!feof(yyin));
    return 0;
}
